#include "backup.h"
// 用于画进度条的两个全局变量。.c文件定义全局变量
long long dirSize = 0; //目录下所有文件的字节数
long long written = 0; //已经写入的字节数e
pthread_mutex_t mutex; //定义互斥锁

long long calculateDirectorySize(const char *path) { //计算目录大小函数
    DIR *dir;
    struct dirent *entry;
    struct stat info;
    long long totalSize = 0;

    // 打开目录
    dir = opendir(path);
    if (dir == NULL) {
        perror("无法打开目录");
        return 0;
    }

    // 遍历目录中的文件和子目录
    while ((entry = readdir(dir)) != NULL) {
        char filePath[PATH_MAX];
        snprintf(filePath, sizeof(filePath), "%s/%s", path, entry->d_name);
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        // 获取文件信息
        if (stat(filePath, &info) == 0) {
            // 判断是否为普通文件
            if (S_ISREG(info.st_mode)) {
                // 累加文件大小
                totalSize += info.st_size;
            }
            // 判断是否为子目录
            else if (S_ISDIR(info.st_mode) && strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                // 递归计算子目录的大小
                totalSize += calculateDirectorySize(filePath);
            }
        }
    }

    // 关闭目录
    closedir(dir);

    return totalSize;
}

int copyPermissions(const char* sourcePath, const char* destinationPath) { //权限分配函数，copyfile函数中,所有文件复制完之后需要调用
    struct stat sourceStat;
    if (stat(sourcePath, &sourceStat) != 0) {
        return -1; // 获取源文件权限失败
    }

    if (chmod(destinationPath, sourceStat.st_mode) != 0) {
        return -1; // 设置目标文件权限失败
    }

    return 0; // 成功复制权限
}

void delete_files_in_directory(const char* directory) { //负责清空dest目录,仅"copy"任务需要
    DIR* dir = opendir(directory);
    if (dir == NULL) {
        printf("Failed to open directory: %s\n", directory);
        return;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char path[256];
        snprintf(path, sizeof(path)+1, "%s/%s", directory, entry->d_name);

        if (entry->d_type == DT_DIR) {
            // 递归删除子目录
            delete_files_in_directory(path);
            // 删除空目录
            rmdir(path);
        } else {
            // 删除文件
            remove(path);
        }
    }

    closedir(dir);
}

void *draw_progress_bar_thread(void *arg) {
    //draw_progress_bar(bytesWritten, totalSize, 40);
    int bar_width = 40;
    while (written <= dirSize) {
        float percentage = (float)written / dirSize; //当前进度的百分比
        int filled_width = bar_width * percentage; //需要填充的占比
        printf("\r[");
        int i;
        for (i = 0; i < filled_width; i++) {
            printf("#");
        }
        for (; i < bar_width; i++) {
            printf(" ");
        }
        printf("] %.2f%%", percentage * 100);
        fflush(stdout);
        if (percentage == 1) break;
    }
     printf("\n");

    pthread_exit(NULL);
}

void *copy_bigfile_thread(void* arg) { //大文件的写入线程
    // printf("新线程\n");
    pthread_mutex_lock(&mutex); //上锁
    ThreadData* data = (ThreadData*)arg;
    // 重新定位文件头指针，为读写做准备
    fseek(data->source, data->start_offset, SEEK_SET);
    fseek(data->dest, data->start_offset, SEEK_SET);

    int buffer_size = data->end_offset - data->start_offset;
    char* buffer = malloc(buffer_size);

    //记录写的字节数，为画进度条作准备
    long bytes_read, bytes_written;
    long total_bytes_written = 0;

    while ((bytes_read = fread(buffer, 1, buffer_size, data->source)) > 0) {
        bytes_written = fwrite(buffer, 1, bytes_read, data->dest);
        total_bytes_written += bytes_written;
        written += bytes_written;
        if (total_bytes_written >= buffer_size) { //段读写不会停止，需要手动终止
            break;
        }
    }
    // printf("线程结束\n");
    pthread_mutex_unlock(&mutex); // 解锁
    free(buffer);
    pthread_exit(NULL);
}

void copy_file(const char* source_file, const char* dest_file) {

    FILE* source = fopen(source_file, "rb");
    FILE* dest = fopen(dest_file, "wb");
    
    // 定位文件，算出文件大小
    fseek(source, 0, SEEK_END);
    long file_size = ftell(source);
    fseek(source, 0, SEEK_SET);

    if (file_size <= FILE_MAX) {  // 小于等于500MB，使用单线程拷贝
        int buffer_size = BUFFER_SIZE;
        char* buffer = malloc(buffer_size);

        long bytes_read, bytes_written;
        
        pthread_mutex_lock(&mutex);
        while ((bytes_read = fread(buffer, 1, buffer_size, source)) > 0) {
            bytes_written = fwrite(buffer, 1, bytes_read, dest);
            written += bytes_written;
        }
        pthread_mutex_unlock(&mutex);
        free(buffer);
    } else {  // 大于500MB，使用多线程拷贝
        // printf("\n文件过大，将开启多线程\n");
        pthread_t threads[THREAD_COUNT];
        ThreadData thread_data[THREAD_COUNT];

        // 每个线程的字节大小，大致的一个数据，因为会有余数，所以要对最后一个线程进行单独判断
        long chunk_size = file_size / THREAD_COUNT;

        for (int i = 0; i < THREAD_COUNT; i++) {
            thread_data[i].source = source;
            thread_data[i].dest = dest;
            thread_data[i].start_offset = i * chunk_size;
            // 对最后一个线程的末尾标记进行单独判断
            thread_data[i].end_offset = (i == THREAD_COUNT -1) ? file_size : (i + 1) * chunk_size;
            pthread_create(&threads[i], NULL, copy_bigfile_thread, (void*)&thread_data[i]);

        }

        for (int i = 0; i < THREAD_COUNT; i++) {
            pthread_join(threads[i], NULL);
        }
    }

    //printf("\nFile copy complete.\n");
    //将源文件的权限赋给目标文件
    copyPermissions(source_file, dest_file);

    fclose(source);
    fclose(dest);
}

void backup_directory(const char* source, const char* destination) { //备份目录函数，传入源目录和目标目录
    DIR* dir = opendir(source);
    if (dir == NULL) {
        printf("Failed to open source directory.\n");
        return;
    }
    struct dirent* entry;
    while ((entry = readdir(dir)) != NULL) { //遍历source的所有子项
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char source_path[MAX_PATH_LENGTH];
        char dest_path[MAX_PATH_LENGTH];
        snprintf(source_path, sizeof(source_path)+1, "%s/%s", source, entry->d_name); //source下子项路径名
        snprintf(dest_path, sizeof(dest_path)+1, "%s/%s", destination, entry->d_name); //destination下子项路径名

        struct stat st;
        lstat(source_path, &st);

        if (S_ISDIR(st.st_mode)) { //如果子项是目录的话，就递归调用backup_directory函数
            mkdir(dest_path, st.st_mode & 0777);
            backup_directory(source_path, dest_path);
        } else {
            copy_file(source_path, dest_path); //copy函数，传入源文件路径 和 目标文件路径
        }
    }

    closedir(dir);
}

void *backup_directory_thread(void* arg) { //线程函数， 传入一个自定义的BackupTask结构体
    BackupTask* task = (BackupTask*)arg;
    delete_files_in_directory(task->destination); //先清空目标目录
    backup_directory(task->source, task->destination); //备份目录函数
    pthread_exit(NULL);
}
